package com.freecloud.bittorrent.demo.service;

import cn.hutool.core.util.ObjectUtil;
import com.freecloud.bitorrent.common.util.NodeIdUtil;
import com.freecloud.bittorrent.demo.DownloadBTFileMain;
import com.freecloud.bittorrent.demo.constant.DhtConstant;
import com.freecloud.bittorrent.demo.handler.DHTHandler;
import com.freecloud.bittorrent.demo.po.KRPC;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.*;

/**
 * 模拟DHT节点服务器
 *
 * @Author: maomao
 * @Date: 2021/11/25 10:23 上午
 */
@Slf4j
public class DHTServer {

    /**
     * 启动节点列表
     */
    public static final List<InetSocketAddress> BOOTSTRAP_NODES = new ArrayList<>(
        Arrays.asList(new InetSocketAddress("router.bittorrent.com", 6881),
            new InetSocketAddress("dht.transmissionbt.com", 6881), new InetSocketAddress("router.utorrent.com", 6881),
            new InetSocketAddress("dht.aelitis.com", 6881)));
    /**
     * dht逻辑
     */
    @Getter
    private DHTHandler dhtHandler = new DHTHandler();
    /**
     * 本地端口
     */
    private int port = 8866;
    private ChannelFuture cahannelFuture;

    private static void findNode(InetSocketAddress address, byte[] requestNid, byte[] selfNodeId) {
        Map<String, Object> argsMap = new HashMap<>();
        argsMap.put("target", selfNodeId);
        if (ObjectUtil.isNotNull(requestNid)) {
            argsMap.put("id", NodeIdUtil.getNeighbor(DhtConstant.SELF_NODE_ID, selfNodeId));
        }
        KRPC krpc = new KRPC(null, selfNodeId, "q", "find_node", argsMap, address);
        log.info("findNode : {}", krpc);
        DownloadBTFileMain.dhtServer.sendKRPC(krpc.getDatagramPacket());
    }

    public void joinDHT() {
        for (InetSocketAddress addr : DHTServer.BOOTSTRAP_NODES) {
            log.info("加入DHT网络：addr: {} self_nodeId:{}", addr, DhtConstant.SELF_NODE_ID);
            findNode(addr, null, DhtConstant.SELF_NODE_ID);
        }
    }

    public void start() {
        //boss线程
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //创建netty服务器对象
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(bossGroup)
                //配置主线程处理逻辑
                .channel(NioDatagramChannel.class)
                //子线程的回调处理
                .handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel client) throws Exception {
                        //处理回调逻辑，注意责任链顺序
                        //链式编程，责任链模式
                        //响应结果编码
                        //client.pipeline().addLast(new HttpResponseEncoder());
                        //用户请求解码
                        //client.pipeline().addLast(new HttpRequestDecoder());
                        //自定义业务逻辑
                        client.pipeline().addLast(dhtHandler);
                    }
                })
                //配置主线程的最大线程数
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_BROADCAST, true).option(ChannelOption.SO_RCVBUF, 268435456)
                .option(ChannelOption.SO_SNDBUF, 65536);
            cahannelFuture = bootstrap.bind(this.port).sync();
            log.info("打印内容：{}", this.port);
            System.out.println("Netty downloadBt start 监听端口：" + this.port);
            //加入DHT网络
            //            joinDHT();
            cahannelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("连接错误：{}", e);
        } finally {
            bossGroup.shutdownGracefully();
        }
    }

    /**
     * 发送KRPC协议数据报文
     *
     * @param packet
     */
    public void sendKRPC(DatagramPacket packet) {
        cahannelFuture.channel().writeAndFlush(packet);
    }
}
